home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 11 / CU Amiga Magazine's Super CD-ROM 11 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-06].iso / cucd / programming / oberonv4 / source / system / kernel.mod (.txt) < prev    next >
Oberon Text  |  1996-04-24  |  28KB  |  678 lines

  1. Syntax10.Scn.Fnt
  2. ParcElems
  3. Alloc
  4. Syntax24b.Scn.Fnt
  5. Syntax10i.Scn.Fnt
  6. StampElems
  7. Alloc
  8. 24 Apr 96
  9. Syntax10b.Scn.Fnt
  10. Syntax12b.Scn.Fnt
  11. (* AMIGA *)
  12. MODULE Kernel;    (* jt/su 90-92, cn/shml 24 May 93, 
  13. (* WARNING: do not use NEW nor SYSTEM.NEW in this module !!
  14.     << added jt/mh/shml/bh's finalization, shml, 28 Jul 94
  15.     << added incremental heap block allocation, shml, 21 Sep 94
  16. IMPORT
  17.     SYSTEM,Amiga,Dos:=AmigaDos;
  18. CONST
  19.     ModNameLength*=24;
  20.     nofLists=9; Unit=16;
  21.     MarkBit*=0;
  22.     sysblk=1; freeblk=2;
  23.     nextOff=4; (* next in free block *)
  24.     hOff=Unit; (* NOTE: The implementation assumes hOff MOD Unit=0! *)
  25.     hSizeOff=0; hNextOff=4;
  26.     (*<< offset of first usable word, size of Amiga block, next Amiga block*) 
  27.     Nil=LONG(LONG(0));
  28.     MaxExts=7;
  29.     ptrTabOffset=40+(MaxExts+1)*4;
  30.     maxcand=1000;
  31.     BlockSize=65536 (*2000000*);    (*<<*) 
  32.     TYPE
  33.         ModuleName*=ARRAY ModNameLength OF CHAR;
  34.         Module*=POINTER TO ModuleDescriptor;
  35.         ModuleDescriptor*=RECORD
  36.             link*:Module;
  37.             nofentries*, nofcoms*, nofptrs*, nofimps*:INTEGER;
  38.             refcnt*:INTEGER;
  39.             constSize*, dataSize*, codeSize*, refSize*:LONGINT;
  40.             key*:LONGINT;
  41.             name*:ModuleName;
  42.             entries*, commands*, pointers*, imports*, const*, data*, code*, refs*:LONGINT
  43.         END;
  44.         Finalizer*=PROCEDURE(obj:SYSTEM.PTR);    (*<<*)
  45.         FinObject=POINTER TO FinObjectDesc;    (*<<*)
  46.         FinObjectDesc=RECORD    (*<<*)
  47.             next:FinObject;
  48.             obj:LONGINT;
  49.             finalize:Finalizer
  50.         END;
  51.         (*<<Sweeper=PROCEDURE;*)
  52.         ADDRESS=LONGINT;
  53.         HeapBlock=RECORD    (*<< allocated from Amiga system *) 
  54.             size:LONGINT;    (* size of this heap block *) 
  55.             next:ADDRESS    (* address of next HeapBlock *) 
  56.         END;
  57.         modules*:Module; (* anchor of module list *)
  58.         GCenabled*: BOOLEAN; (*<< enable/disable GC; RD *)
  59.         (* oberon heap management *)
  60.         heap-:LONGINT;
  61. List of memory blocks allocated from the host operating system. This list is somehow unusual, as head doesn't point to the real beginning of a block. Instead it points to an address after the header, where the "Oberon managed portion" of the block starts. The list is sorted so that blocks at a lower memory address preceed blocks with a larger memory address.
  62.         heapSize-:LONGINT; (* Record the total size requested from the host operating system. *)
  63.         allocated-:LONGINT; (* Record the size of all allocations within Oberon. *)
  64.         nofiles*,stackBottom*:LONGINT;
  65.         freeList:ARRAY nofLists+1 OF ADDRESS; (* dummy,16,32,48,64,80,96,112,128,sentinel *)
  66.             This maintains separate free lists for small blocks. Thus for small allocations
  67.             a good fitting block is found easily.
  68.         bigBlocks:ADDRESS;
  69.             This is the list of "Oberon managed blocks".
  70.         heapEnd:ADDRESS;    (*<<*) 
  71.             points to the first address after the last memory block. Thus all heap managed memory
  72.             is contained in the memory area between heap and heapEnd.
  73.         firstTry:BOOLEAN;
  74.         fin:FinObject; (* List of registered objects for finalization *)
  75.         toBeFin:FinObject; (* List of unreferenced objects which have to be finalized *)    (*<<*)
  76.             Procedure vars for F keys 
  77.         FKey*: ARRAY 16 OF PROCEDURE;    (*<RD*)
  78.     PROCEDURE^ GC*(markStack:BOOLEAN);
  79.     PROCEDURE^ AllocHeap(VAR adr:LONGINT; size:LONGINT);
  80.     PROCEDURE New*(tag:ADDRESS):ADDRESS;
  81.     VAR i,i0,di,size,restsize,t:LONGINT; adr,next,prev:ADDRESS;
  82.     BEGIN
  83.         SYSTEM.GET(tag,size);
  84.         i0:=size DIV Unit; i:=i0;
  85.             Try to locate a free block in one of the small block lists, starting
  86.             with the best fitting one and working up to bigger ones, as long
  87.             as no free blocks are found.
  88.         IF i<nofLists THEN
  89.             adr:=freeList[i];
  90.             WHILE adr=Nil DO INC(i); adr:=freeList[i] END; (* This terminates because of sentinel. *)
  91.         END ;
  92.         IF i<nofLists THEN (* unlink *)
  93.             (*
  94.                 A small block was found. It is unlinked from the free list.
  95.             *)
  96.             SYSTEM.GET(adr+nextOff,next);
  97.             freeList[i]:=next;
  98.             IF i#i0 THEN (* split *)
  99.                 (*
  100.                     The block was not the smallest possible. Thus it is split. The 
  101.                     first part forms the remaining free block, which is linked into 
  102.                     the appropriate free list. The second part forms the block
  103.                     which is returned from New. 
  104.                 *)
  105.                 di:=i - i0; restsize:=di*Unit;
  106.                 SYSTEM.PUT(adr,restsize+ASH(1,freeblk));
  107.                 SYSTEM.PUT(adr+nextOff,freeList[di]);
  108.                 freeList[di]:=adr;
  109.                 INC(adr,restsize)
  110.             END;
  111.             SYSTEM.PUT(adr,size+ASH(1,freeblk));
  112.         ELSE
  113.             (*
  114. No free block of the small free blocks could be used, so one of the "big blocks" is taken. Eventually, this means requesting new blocks from the host operating system.
  115. To describe the following code, its best to look at the possible szenarios:
  116. 1)    bigBlocks contains a block large enough. This block is found, and the loop terminates at the EXIT statement. After some extra work, New will terminate.
  117. 2)    bigBlocks is emtpy and a garbage collection won't change anything. In this case New is recursively called, with the global firstTry set to FALSE. The second invocation will, after traversing the bigBlocks list withou success, enter the ELSE-part of the IF firstTry statement. AllocHeap will allocate a memory block large enough to satisy the request and link it into the bigBlock list. New is invoked a third time. This third invocation will find the just allocated block in the list, and exit the loop through the EXIT statement, do the extra work and return. The RETURN adr is executed, terminating the second invocation. This is followed by a resetting of firstTry to TRUE and a RETURN adr, which terminates the main invocation of New.
  118. 3)    bigBlocks is empty, but garbage collection will return some useful space. The invocation of New following the GC call will find this space either in one of the small block free lists, or in the big free list. In none of these cases it will enter the IF adr=Nil statement, but terminate regularly. After this invocation returns, the main New invocation is terminated.
  119.             *)
  120.             adr:=bigBlocks; prev:=Nil;
  121.             LOOP
  122.                 IF adr=Nil THEN
  123.                     IF firstTry THEN
  124.                         GC(TRUE); firstTry:=FALSE; adr:=New(tag); firstTry:=TRUE; RETURN adr
  125.                     ELSE
  126.                         AllocHeap(adr, size);    (*<<*) 
  127.                         IF adr=Nil THEN RETURN Nil    (*<<*)
  128.                         ELSE adr:=New(tag); RETURN adr    (*<<*)
  129.                         END
  130.                     END
  131.                 END ;
  132.                 SYSTEM.GET(adr,t);
  133.                 IF t >=size THEN EXIT END ; (* why not IF t>=size ?? *)
  134.                 prev:=adr;
  135.                 SYSTEM.GET(adr+nextOff,adr);
  136.             END ;
  137.             (*
  138. A block large enough is located. If the unneeded size is larger than the blocks managed in the freelist, it is simply shortened. Otherwise it is completely unliked, and the initial remaining block linked into the appropriate small block free list. 
  139.             *)
  140.             restsize:=t - size - ASH(1,freeblk);
  141.             SYSTEM.PUT(adr+restsize,size+ASH(1,freeblk));
  142.             IF restsize >= nofLists*Unit THEN (*resize*) (* Shouldn't it be restsize >= nofLists*Unit ??? *)
  143.                 SYSTEM.PUT(adr,restsize+ASH(1,freeblk))
  144.             ELSE (*unlink*)
  145.                 SYSTEM.GET(adr+nextOff,next);
  146.                 IF prev=Nil THEN bigBlocks:=next
  147.                 ELSE SYSTEM.PUT(prev+nextOff,next)
  148.                 END ;
  149.                 IF restsize > 0 THEN (*move*)
  150.                     di:=restsize DIV Unit;
  151.                     SYSTEM.PUT(adr,restsize+ASH(1,freeblk));
  152.                     SYSTEM.PUT(adr+nextOff,freeList[di]);
  153.                     freeList[di]:=adr
  154.                 END
  155.             END ;
  156.             INC(adr,restsize)
  157.         END ;
  158.             Erase the allocated block, and put the type tag at the beginning of the block.
  159.         i:=4; WHILE i<size DO SYSTEM.PUT(adr+i,Nil); INC(i,4) END ;
  160.         SYSTEM.PUT(adr,tag);
  161.         INC(allocated,size);
  162.         RETURN adr+4;
  163.     END New;
  164.     PROCEDURE SysNew*(size:LONGINT):ADDRESS;
  165.         VAR i,i0,di,restsize,t:LONGINT; adr,next,originalSize,prev:ADDRESS;
  166.     BEGIN
  167.         originalSize:=size;
  168.         INC(size,12); INC(size,(-size) MOD Unit);
  169.         i0:=size DIV Unit; i:=i0;
  170.             Try to locate a free block in one of the small block lists, starting
  171.             with the best fitting one and working up to bigger ones, as long
  172.             as no free blocks are found.
  173.         IF i<nofLists THEN
  174.             adr:=freeList[i];
  175.             WHILE adr=Nil DO INC(i); adr:=freeList[i] END; (* This terminates because of sentinel. *)
  176.         END ;
  177.         IF i<nofLists THEN (* unlink *)
  178.             (*
  179.                 A small block was found. It is unlinked from the free list.
  180.             *)
  181.             SYSTEM.GET(adr+nextOff,next);
  182.             freeList[i]:=next;
  183.             IF i#i0 THEN (* split *)
  184.                 (*
  185.                     The block was not the smallest possible. Thus it is split. The 
  186.                     first part forms the remaining free block, which is linked into 
  187.                     the appropriate free list. The second part forms the block
  188.                     which is returned from SysNew. 
  189.                 *)
  190.                 di:=i - i0; restsize:=di*Unit;
  191.                 SYSTEM.PUT(adr,restsize+ASH(1,freeblk));
  192.                 SYSTEM.PUT(adr+nextOff,freeList[di]);
  193.                 freeList[di]:=adr;
  194.                 INC(adr,restsize)
  195.             END;
  196.             SYSTEM.PUT(adr,size+ASH(1,freeblk))
  197.         ELSE
  198.             (*
  199.                 For a description, see the analoguos part in New().
  200.             *)
  201.             adr:=bigBlocks; prev:=Nil;
  202.             LOOP
  203.                 IF adr=Nil THEN
  204.                     IF firstTry THEN
  205.                         GC(TRUE); firstTry:=FALSE; adr:=SysNew(originalSize); firstTry:=TRUE; RETURN adr
  206.                     ELSE
  207.                         AllocHeap(adr, size);    (*<<*) 
  208.                         IF adr=Nil THEN RETURN Nil    (*<<*)
  209.                         ELSE adr:=SysNew(originalSize); RETURN adr    (*<<*)
  210.                         END
  211.                     END
  212.                 END ;
  213.                 SYSTEM.GET(adr,t);
  214.                 IF t >= size THEN EXIT END ; (* why not IF t>=size ?? *)
  215.                 prev:=adr; 
  216.                 SYSTEM.GET(adr+nextOff,adr)
  217.             END ;
  218.             (*
  219. A block large enough is located. If the unneeded size is larger than the blocks managed in the freelist, it is simply shortened. Otherwise it is completely unliked, and the initial remaining block linked into the appropriate small block free list. 
  220.             *)
  221.             restsize:=t - size - ASH(1,freeblk);
  222.             SYSTEM.PUT(adr+restsize,size+ASH(1,freeblk));
  223.             IF restsize >=nofLists*Unit THEN (*resize*) (* Shouldn't it be restsize >= nofLists*Unit ??? *)
  224.                 SYSTEM.PUT(adr,restsize+ASH(1,freeblk))
  225.             ELSE (*unlink*)
  226.                 SYSTEM.GET(adr+nextOff,next);
  227.                 IF prev=Nil THEN bigBlocks:=next
  228.                 ELSE SYSTEM.PUT(prev+nextOff,next)
  229.                 END ;
  230.                 IF restsize > 0 THEN (*move*)
  231.                     di:=restsize DIV Unit;
  232.                     SYSTEM.PUT(adr,restsize+ASH(1,freeblk));
  233.                     SYSTEM.PUT(adr+nextOff,freeList[di]);
  234.                     freeList[di]:=adr
  235.                 END
  236.             END ;
  237.             INC(adr,restsize)
  238.         END ;
  239.             The type tag points to the end of the block, where just the size is
  240.             stored (a pseudo type tag?). To distinguish this block from a regular
  241.             one, the sysblock flag is set.
  242.             Note: These blocks are not zeroed.
  243.         i:=adr+size - 8;
  244.         SYSTEM.PUT(i,size);
  245.         SYSTEM.PUT(adr,i+ASH(1,sysblk));
  246.         RETURN adr+4
  247.     END SysNew;
  248.     PROCEDURE RegisterObject*(obj:SYSTEM.PTR; finalize:Finalizer);    (*<<*)
  249.         f:FinObject;
  250.         PROCEDURE new (VAR finObj:SYSTEM.PTR);    (* finObj is initialized with tag, hack! *) 
  251.         BEGIN
  252.             finObj:=SYSTEM.VAL(SYSTEM.PTR,New(SYSTEM.VAL(ADDRESS,finObj)))
  253.         END new;
  254.     BEGIN
  255.         IF obj#NIL THEN
  256.             new(f);
  257.             f.obj:=SYSTEM.VAL(LONGINT,obj);
  258.             f.finalize:=finalize;
  259.             f.next:=fin;
  260.             fin:=f;
  261.         END;
  262.     END RegisterObject;
  263.     PROCEDURE^ Mark(q:ADDRESS);
  264.     PROCEDURE CheckFin;    (*<<*)
  265.         f, prev, next:FinObject;
  266.         tag:SET;
  267.     BEGIN
  268.             For each object in the finalization list, check if it is marked.
  269.             If not, mark it to prevent Sweep() from freeing it, and move the
  270.             finalization object to the list of finalizations which have to 
  271.             be performed.
  272.         f:=fin; prev:=NIL;
  273.         WHILE f#NIL DO
  274.             next:=f.next;
  275.             SYSTEM.GET(f.obj-4, tag);
  276.             IF ~(MarkBit IN tag) THEN    (* garbage object, put it into to-be-finalized list *)
  277.                 Mark(SYSTEM.VAL(ADDRESS, f.obj));    (* mark f.obj and all objects accessible from it *)
  278.                 IF prev=NIL THEN fin:=next ELSE prev.next:=next END;
  279.                 f.next:=toBeFin; toBeFin:=f;
  280.             ELSE
  281.                 prev:=f;
  282.             END;
  283.             f:=next
  284.         END;
  285.     END CheckFin;
  286.     PROCEDURE Finalize;    (*<<*)
  287.         f:FinObject;
  288.     BEGIN
  289.         WHILE toBeFin#NIL DO
  290.             f:=toBeFin; 
  291.             toBeFin:=toBeFin.next; f.finalize(SYSTEM.VAL(SYSTEM.PTR, f.obj))
  292.         END;
  293.     END Finalize;
  294.     PROCEDURE FinalizeAll;    (*<<*) 
  295.         f:FinObject;
  296.     BEGIN
  297.         f:=fin;
  298.         WHILE f#NIL DO
  299.             f.finalize(SYSTEM.VAL(SYSTEM.PTR, f.obj));
  300.             f:=f.next;
  301.         END;
  302.     END FinalizeAll;
  303.     PROCEDURE Mark(q:ADDRESS);
  304.         VAR p,tag,fld,n:ADDRESS; offset:LONGINT; tagbits:SET;
  305.     BEGIN
  306.         IF q#Nil THEN
  307.             (* If pointer not NIL then get tagbits. *)
  308.             SYSTEM.GET(q - 4,tagbits);
  309.             IF ~(MarkBit IN tagbits) THEN
  310.                 (* If not yet marked, then mark now. *)
  311.                 SYSTEM.PUT(q - 4,tagbits+{MarkBit});
  312.                 IF ~(sysblk IN tagbits) THEN
  313.                     (* If not a block allocate with SysNew() then ... *)
  314.                     p:=Nil;
  315.                     tag:=SYSTEM.VAL(LONGINT,tagbits)+ptrTabOffset;
  316.                     LOOP
  317.                         SYSTEM.GET(tag,offset);
  318.                         IF offset<0 THEN
  319.                             SYSTEM.PUT(q - 4,tag+offset+ASH(1,MarkBit));
  320.                             IF p=Nil THEN EXIT END ;
  321.                             n:=q; q:=p;
  322.                             SYSTEM.GET(q - 4,tag);
  323.                             DEC(tag,ASH(1,MarkBit));
  324.                             SYSTEM.GET(tag,offset);
  325.                             fld:=q+offset;
  326.                             SYSTEM.GET(fld,p); SYSTEM.PUT(fld,n)
  327.                         ELSE
  328.                             fld:=q+offset;
  329.                             SYSTEM.GET(fld,n);
  330.                             IF n#Nil THEN
  331.                                 SYSTEM.GET(n - 4,tagbits);
  332.                                 IF ~(MarkBit IN tagbits) THEN
  333.                                     SYSTEM.PUT(n - 4,tagbits+{MarkBit});
  334.                                     IF ~(sysblk IN tagbits) THEN
  335.                                         SYSTEM.PUT(q - 4,tag+ASH(1,MarkBit));
  336.                                         SYSTEM.PUT(fld,p);
  337.                                         p:=q;
  338.                                         q:=n;
  339.                                         tag:=SYSTEM.VAL(LONGINT,tagbits)+(ptrTabOffset - 4);
  340.                                     END
  341.                                 END
  342.                             END
  343.                         END ;
  344.                         INC(tag,4);
  345.                     END
  346.                 END
  347.             END
  348.         END;
  349.     END Mark;
  350.     PROCEDURE Sweep;
  351.         VAR heapBlock, prev, this, adr, end, start:ADDRESS; tag:SET; i, size, freesize, tagv:LONGINT; thisBlock:HeapBlock;
  352.     BEGIN
  353.             Clear the free lists and reset the size of allocations.
  354.         i:=1;
  355.         WHILE i<nofLists DO freeList[i]:=Nil; INC(i) END ;
  356.         bigBlocks:=Nil;
  357.         (*<< adr:=heap; end:=adr+heapsize; freesize:=0; *)
  358.         allocated:=0;
  359.             Walk through all blocks and rebuild free list.
  360.             Note:
  361.                 heapBlock, this and thisBlock point to the start of the whole block.
  362.                 heap points to the start of the Oberon managed part.
  363.                 adr points to the current block within the Oberon managed part.
  364.         heapBlock:=heap-hOff; prev:=Nil;    (*<<*) 
  365.         WHILE heapBlock#Nil DO    (*<<*) 
  366.             freesize:=0;
  367.             this:=heapBlock;    (*<<*)
  368.             SYSTEM.MOVE(this, SYSTEM.ADR(thisBlock), SIZE(HeapBlock));    (*<<*) 
  369.             (*
  370.                 Go through all Oberon managed blocks within this memory block.
  371.             *)
  372.             adr:=this+hOff;
  373.             end:=this+thisBlock.size;    (*<<*) 
  374.             WHILE adr<end DO
  375.                 SYSTEM.GET(adr,tag);
  376.                 tagv:=SYSTEM.VAL(LONGINT, tag - {freeblk, sysblk, MarkBit});
  377.                 IF freeblk IN tag THEN
  378.                     (*
  379.                         The tag of a free block contains directly the size. Add it
  380.                         to the free size and go to the next block.
  381.                     *)
  382.                     INC(freesize,tagv); INC(adr,tagv);
  383.                 ELSIF MarkBit IN tag THEN
  384.                     (*
  385.                         A marked block is kept. It is unmarked, to restore the normal
  386.                         conditions. All freeblocks encountered before this marked 
  387.                         block are combined into one, and according to there size 
  388.                         stored into the appropriate free list.
  389.                     *)
  390.                     SYSTEM.PUT(adr, tag - {MarkBit});
  391.                     IF freesize > 0 THEN
  392.                         start:=adr - freesize;
  393.                         SYSTEM.PUT(start,freesize+ASH(1,freeblk));
  394.                         IF freesize<nofLists*Unit THEN
  395.                             i:=freesize DIV Unit; 
  396.                             SYSTEM.PUT(start+nextOff,freeList[i]); freeList[i]:=start
  397.                         ELSE
  398.                             SYSTEM.PUT(start+nextOff,bigBlocks); bigBlocks:=start
  399.                         END ;
  400.                         freesize:=0;
  401.                     END ;
  402.                     (*
  403.                         Increment the size of allocated memory by the size of this 
  404.                         marked block and go to the next block.
  405.                     *)
  406.                     SYSTEM.GET(tagv,size);
  407.                     INC(allocated,size); INC(adr,size)
  408.                 ELSE (*unmarked*)
  409.                     (*
  410.                         A block which isn't marked, and thus not used anymore.
  411.                         Add its size to the size of free blocks and goto the next one.
  412.                     *)
  413.                     SYSTEM.GET(tagv,size);
  414.                     INC(freesize,size); INC(adr,size);
  415.                 END
  416.             END;
  417.             (*
  418.                 Go to the next memory block. The current block is unlinked, if no
  419.                 marked blocks where in it, i.e. if it consisted of a sequence of free
  420.                 or unmarked blocks, which totalized a freesize equal of the blocks
  421.                 size.
  422.             *)
  423.             heapBlock:=thisBlock.next;    (*<<*)
  424.             IF freesize=thisBlock.size-hOff THEN    (*<< the whole block is empty, deallocate it*)
  425.                 IF this=heap-hOff THEN    (* first heap block becomes empty, still at least one block left *) 
  426.                     heap:=heapBlock+hOff    (* thisBlock.next *) 
  427.                 ELSE    (* previous.next:=thisBlock.next *)
  428.                     SYSTEM.PUT(prev+hNextOff, heapBlock);    (* prev.next:=this.next *) 
  429.                     (*
  430.                         If there is any following block, then we unlinked the last one of
  431.                         the list. Thus we have to adjust heapEnd to point to the first
  432.                         byte after the previous block.
  433.                     *)
  434.                     IF heapBlock=Nil THEN
  435.                         SYSTEM.GET(prev+hSizeOff, heapEnd); INC(heapEnd, prev)    (* heapEnd:=prev.size+prev *)
  436.                     END
  437.                 END;
  438.                 (*
  439.                     The block is returned to the host operating system, and the size of
  440.                     used host storage is reduced.
  441.                 *)
  442.                 Amiga.Deallocate(this, thisBlock.size); DEC(heapSize, thisBlock.size);
  443.                 freesize:=0;
  444.             ELSE
  445.                 IF freesize > 0 THEN (*collect last block*)
  446.                     start:=adr - freesize;
  447.                     SYSTEM.PUT(start,freesize+ASH(1, freeblk));
  448.                     IF freesize<nofLists*Unit THEN
  449.                         i:=freesize DIV Unit;
  450.                         SYSTEM.PUT(start+nextOff, freeList[i]); freeList[i]:=start
  451.                     ELSE
  452.                         SYSTEM.PUT(start+nextOff, bigBlocks); bigBlocks:=start
  453.                     END;
  454.                 END;
  455.                 prev:=this
  456.             END 
  457.         END;    (*<< WHILE heapBlock#Nil *) 
  458.     END Sweep;
  459.     PROCEDURE Sift (l,r:LONGINT; VAR a:ARRAY OF LONGINT);
  460.         VAR i,j,x:LONGINT;
  461.     BEGIN
  462.         j:=l; x:=a[j];
  463.         LOOP
  464.             i:=j; j:=2*j+1;
  465.             IF (j<r) & (a[j]<a[j+1]) THEN INC(j) END;
  466.             IF (j > r) OR (a[j] <= x) THEN EXIT END;
  467.             a[i]:=a[j]
  468.         END;
  469.         a[i]:=x
  470.     END Sift;
  471.     PROCEDURE HeapSort (n:LONGINT; VAR a:ARRAY OF LONGINT);
  472.         VAR l,r,x:LONGINT;
  473.     BEGIN
  474.         l:=n DIV 2; r:=n - 1;
  475.         WHILE l > 0 DO DEC(l); Sift(l,r,a) END;
  476.         WHILE r > 0 DO x:=a[0]; a[0]:=a[r]; a[r]:=x; DEC(r); Sift(l,r,a) END;
  477.     END HeapSort;
  478.     PROCEDURE MarkCandidates(n:LONGINT; VAR cand:ARRAY OF LONGINT);
  479.         VAR adr,heapBlock,heapBlockEnd,next,lim:ADDRESS; i,ptr,tagv:LONGINT; tag:SET;
  480.     BEGIN
  481.         adr:=heap; i:=0; lim:=cand[n-1];
  482.         IF ODD(lim) THEN DEC(lim); END;
  483.         heapBlock:=heap-hOff;    (*<<*) 
  484.         SYSTEM.GET(heapBlock+hSizeOff, heapBlockEnd); INC(heapBlockEnd, heapBlock);    (*<<*) 
  485.         WHILE adr <= lim DO
  486.             SYSTEM.GET(adr,tag);
  487.             tagv:=SYSTEM.VAL(LONGINT,tag - {freeblk, sysblk, MarkBit}); 
  488.             IF MarkBit IN tag THEN SYSTEM.GET(tagv, tagv); INC(adr, tagv)    (* if marked block, skip *) 
  489.             ELSIF freeblk IN tag THEN INC(adr,tagv)    (* if free block, skip *) 
  490.             ELSE    (* not marked, check if stack pointer bound! *) 
  491.                 SYSTEM.GET(tagv,tagv);
  492.                 ptr:=adr+4;
  493.                 WHILE (cand[i]<ptr) & (i<n) DO INC(i) END ; (* Termination was not guaranteed !!! *)
  494.                 IF i=n THEN RETURN END ;
  495.                 next:=adr+tagv;
  496.                 IF cand[i]<next THEN    (* cand[i] points into this block => mark it! *) 
  497.                     IF sysblk IN tag THEN SYSTEM.PUT(adr,tag+{MarkBit}) ELSE Mark(ptr) END
  498.                 END ;
  499.                 adr:=next
  500.             END;
  501.             IF adr >= heapBlockEnd THEN    (*<<*) 
  502.                 SYSTEM.GET(heapBlock+hNextOff, heapBlock);    (* heapBlock:=heapBlock.next *) 
  503.                 IF heapBlock#Nil THEN
  504.                     adr:=heapBlock+hOff;
  505.                     SYSTEM.GET(heapBlock+hSizeOff, heapBlockEnd); INC(heapBlockEnd, heapBlock);
  506.                 ELSE RETURN    (* end of Amiga blocks! *) 
  507.                 END
  508.             END
  509.         END;
  510.     END MarkCandidates;
  511.     PROCEDURE GC*(markStack:BOOLEAN);
  512.         VAR frame:RECORD END ;
  513.             m:Module; i,ptrOffset,nofcand:LONGINT;
  514.             sp,p,heapstart,heapend,stackbottom,ptr:ADDRESS;
  515.             cand:ARRAY maxcand OF LONGINT;
  516.     BEGIN
  517.         IF GCenabled THEN
  518.             (*
  519.                 Go through all modules and call Mark for each pointer in
  520.                 each module.
  521.             *)
  522.             m:=modules;
  523.             WHILE m#NIL DO
  524.                 (*Amiga.BreakPoint(m.name);*)
  525.                 i:=0;
  526.                 WHILE i<m^.nofptrs DO
  527.                     SYSTEM.GET(m^.pointers+i*4,ptrOffset);
  528.                     SYSTEM.GET(m^.data+ptrOffset,ptr);
  529.                     Mark(ptr);
  530.                     INC(i)
  531.                 END ;
  532.                 m:=m^.link
  533.             END ;
  534.             IF markStack THEN
  535.                 (*
  536.                     Traverse the step, 16bit word wise and for every 32bit word
  537.                     which  value is within heapstart and heapend use MarkCandidates
  538.                     to mark them. Instead of looking at each of them individually,
  539.                     a group of upto maxcand pointers are collected, sorted and 
  540.                     marked.
  541.                 *)
  542.                 nofcand:=0;
  543.                 sp:=SYSTEM.ADR(frame); 
  544.                 stackbottom:=stackBottom;
  545.                 heapstart:=heap; (*<<heapend:=heapstart+heapsize;*)
  546.                 heapend:=heapEnd;
  547.                 WHILE sp<stackbottom DO
  548.                     SYSTEM.GET(sp,p);
  549.                     IF (heapstart<p) & (p<heapend) THEN
  550.                         IF nofcand=maxcand THEN HeapSort(nofcand,cand); MarkCandidates(nofcand,cand); nofcand:=0 END ;
  551.                         cand[nofcand]:=p; INC(nofcand)
  552.                     END ;
  553.                     INC(sp,2)
  554.                 END ;
  555.                 IF nofcand>0 THEN HeapSort(nofcand,cand); MarkCandidates(nofcand,cand) END
  556.             END ;
  557.             CheckFin;    (*<< put all garbage objects into the to-be-finalized list *)
  558.             (*<< i:=0;
  559.             WHILE (i<nofSweep) & (sweep[i]#NIL) DO sweep[i]; INC(i) END ; *)
  560.             Sweep;
  561.             Finalize;    (*<< call finalize procedure for each garbage object *)
  562.         END;
  563.     END GC;
  564.     PROCEDURE AllocHeap(VAR adr:ADDRESS; size:LONGINT);    (*<<*) 
  565.         (* allocate a new heap chunk from Amiga and insert it into the bigBlocks list of Oberon *) 
  566.         VAR this, prev, next:ADDRESS; alloc:LONGINT;
  567.     BEGIN
  568.             Determine, how much has to be allocated. If the requested size fits in a 
  569.             standard block, then try allocate a standard block but at least Uni*nofLists.
  570.             Otherwise allocate a block which, accounting for the header, is big enough
  571.             to just satisfy the request. The block is adjusted, so that it contains an integral
  572.             number of subblocks of size Unit.
  573.         IF size <= BlockSize-hOff THEN
  574.             alloc:=BlockSize;
  575.             IF size<nofLists*Unit THEN size:=nofLists*Unit END;    (* ensure a chunk of at least nofLists*Unit *) 
  576.             INC(size,hOff); (*<<cn*) (* NOTE: this is correct, because hOff MOD Unit=0! *)
  577.         ELSE
  578.             INC(size, hOff); size:=size+(-size MOD Unit); alloc:=size (* NOTE: this is correct, because hOff MOD Unit=0! *)
  579.         END;
  580.             An allocation is tried. The requested length is reduced from alloc downto no less then size,
  581.             if low memory condition or high fragmentation request for it.
  582.         REPEAT Amiga.Allocate(adr, alloc); alloc:=alloc DIV 2 UNTIL (adr#Nil) OR (alloc<size);
  583.         IF adr#Nil THEN
  584.             (*
  585.                 Herein, size takes the total number of allocated bytes.
  586.             *)
  587.             size:=2*alloc;
  588.             INC(heapSize, size);
  589.             IF heap=Nil THEN
  590.                 next:=Nil; heap:=adr+hOff    (* new first block *)
  591.             ELSE
  592.                 (*
  593.                     Note:
  594.                         adr, prev and this point to the true start of a block.
  595.                         heap points to the start of the Oberon managed part of the block.
  596.                 *)
  597.                 this:=heap-hOff; prev:=Nil;
  598.                 WHILE (this<adr) & (this#Nil) DO    (* find insertion point for this heap block in block list *) 
  599.                     prev:=this;
  600.                     SYSTEM.GET(this+hNextOff, this)    (* this:=this.next *)
  601.                 END;
  602.                 IF prev=Nil THEN next:=heap-hOff; heap:=adr+hOff    (* adr is new first block *) 
  603.                 ELSE next:=this; SYSTEM.PUT(prev+hNextOff, adr);    (* prev.next:=adr *) 
  604.                 END
  605.             END;
  606.             SYSTEM.PUT(adr+hSizeOff, size);    (* adr.size:=size *) 
  607.             SYSTEM.PUT(adr+hNextOff, next);    (* adr.next:=next *) 
  608.             IF next=Nil THEN heapEnd:=adr+size; END;    (* end of list, we just appended the last block *) 
  609.             INC(adr, hOff);    (* points to Oberon part of heap block *) 
  610.             (*
  611.                 Each Oberon managed free block starts with a header containing two 32bit words.
  612.                 The first contains the size of the Oberon managed block and some flags in the lower 
  613.                 three bits. One of them marks the block as free.
  614.                 The second word points to the next Oberon managed block. The list of Oberon
  615.                 managed blocks is not sorted, thus this new block is simply inserted at the top of the
  616.                 list.
  617.             *)
  618.             SYSTEM.PUT(adr, size-hOff+ASH(1, freeblk));
  619.             SYSTEM.PUT(adr+nextOff, bigBlocks);    (* link it into bigBlocks list *) 
  620.             bigBlocks:=adr
  621.         END;
  622.     END AllocHeap;
  623.     PROCEDURE Init;
  624.     (* Initializes the Oberon memory management. *)
  625.         VAR
  626.             adr:LONGINT;
  627.             i:LONGINT;
  628.     BEGIN
  629.         stackBottom:=SYSTEM.ADR(adr);
  630.         Amiga.InstallModuleList(SYSTEM.ADR(modules));
  631.         Amiga.InstallNew(New);
  632.         Amiga.InstallSysNew(SysNew);
  633.         FOR i:=0 TO nofLists-1 DO freeList[i]:=Nil END;
  634.         (*<<FOR i:=0 TO nofSweep-1 DO sweep[i]:=NIL END;*)
  635.         freeList[nofLists]:=1; (* sentinel *)
  636.         heapSize:=0; heap:=Nil;    (*<<*) 
  637.         bigBlocks:=Nil;    (*<<cn*)
  638.         AllocHeap(adr, BlockSize-hOff);    (*<<*) 
  639.         firstTry:=TRUE;
  640.         fin:=NIL; toBeFin:=NIL;    (*<<*)
  641.         Amiga.TermProcedure(FinalizeAll);    (*<<cn*)
  642.         GCenabled:=TRUE;    (*<RD*)    
  643.         FOR i:=0 TO 15 DO FKey[i]:=NIL END;    (*<RD*)
  644.     END Init;
  645.     PROCEDURE GetClock*(VAR t,d:LONGINT);
  646.     CONST
  647.         amigaOffset=28430; (* Days between 1.1.1978 and 1.3.1900 *)
  648.         refYear=1900;
  649.     TYPE
  650.         DatePtr=POINTER TO Dos.Date;
  651.         dt:DatePtr;
  652.         dtl,sec,min,hour,day,mon,year:LONGINT;
  653.         buf:RECORD d:Dos.Date; pad:LONGINT END;
  654.     BEGIN
  655.         dtl:=SYSTEM.ADR(buf);
  656.         WHILE (dtl MOD 4)#0 DO INC(dtl) END;
  657.         dt:=SYSTEM.VAL(DatePtr,dtl);
  658.         Dos.DateStamp(dt^);
  659.         sec:=dt.tick DIV Dos.ticksPerSecond;
  660.         min:=dt.minute MOD 60; hour:=dt.minute DIV 60;
  661.         day:=dt.days+amigaOffset;
  662.         year:=(4*day+3) DIV 1461;
  663.         DEC(day,1461*year DIV 4);
  664.         INC(year,refYear);
  665.         mon:=(5*day+2) DIV 153;
  666.         day:=day-(153*mon+2) DIV 5+1;
  667.         INC(mon,3);
  668.         IF mon>12 THEN INC(year); DEC(mon,12) END;
  669.         t:=sec+ASH(min,6)+ASH(hour,12);
  670.         d:=day+ASH(mon,5)+ASH(year MOD 100,9)
  671.     END GetClock;
  672.     PROCEDURE SetClock*(t,d:LONGINT);
  673.     BEGIN
  674. (* Clock setting from Oberon is not allowed. *)
  675.     END SetClock;
  676. BEGIN Init
  677. END Kernel.
  678.